// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// The file defines the symbols from NDKMediaCodec that android is using. It
// then loads the library dynamically on first use.

#include <media/NdkMediaCodec.h>
#include <media/NdkMediaFormat.h>
#include <stddef.h>
#include <stdint.h>

#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/native_library.h"

#define LOOKUP_FUNC(func_name, return_type, args, arg_names)                                                    \
    return_type func_name args                                                                                  \
    {                                                                                                           \
        typedef return_type(*signature) args;                                                                   \
        static signature g_##func_name = reinterpret_cast<signature>(base::GetFunctionPointerFromNativeLibrary( \
            LibraryHandle(), #func_name));                                                                      \
        return g_##func_name arg_names;                                                                         \
    }

// The constants used in chromium. Those symbols are defined as extern symbols
// in the NdkMediaFormat headers. They will be initialized to their correct
// values when the library is loaded.
const char* AMEDIAFORMAT_KEY_CHANNEL_COUNT;
const char* AMEDIAFORMAT_KEY_HEIGHT;
const char* AMEDIAFORMAT_KEY_SAMPLE_RATE;
const char* AMEDIAFORMAT_KEY_WIDTH;

namespace {

// The name of the library to load.
const char kMediaNDKLibraryName[] = "libmediandk.so";

// Loads the OpenSLES library, and initializes all the proxies.
base::NativeLibrary IntializeLibraryHandle()
{
    base::NativeLibrary handle = base::LoadNativeLibrary(base::FilePath(kMediaNDKLibraryName), NULL);
    DCHECK(handle) << "Unable to load " << kMediaNDKLibraryName;

    // Setup the proxy for each symbol.
    // Attach the symbol name to the proxy address.
    struct SymbolDefinition {
        const char* name;
        const char** value;
    };

    // The list of defined symbols.
    const SymbolDefinition kSymbols[] = {
        { "AMEDIAFORMAT_KEY_CHANNEL_COUNT", &AMEDIAFORMAT_KEY_CHANNEL_COUNT },
        { "AMEDIAFORMAT_KEY_HEIGHT", &AMEDIAFORMAT_KEY_HEIGHT },
        { "AMEDIAFORMAT_KEY_SAMPLE_RATE", &AMEDIAFORMAT_KEY_SAMPLE_RATE },
        { "AMEDIAFORMAT_KEY_WIDTH", &AMEDIAFORMAT_KEY_WIDTH },
    };

    for (size_t i = 0; i < sizeof(kSymbols) / sizeof(kSymbols[0]); ++i) {
        memcpy(kSymbols[i].value,
            base::GetFunctionPointerFromNativeLibrary(handle, kSymbols[i].name),
            sizeof(char*));
        DCHECK(*kSymbols[i].value) << "Unable to find symbol for "
                                   << kSymbols[i].name;
    }
    return handle;
}

// Returns the handler to the shared library. The library itself will be lazily
// loaded during the first call to this function.
base::NativeLibrary LibraryHandle()
{
    // The handle is lazily initialized on the first call.
    static base::NativeLibrary g_mediandk_LibraryHandle = IntializeLibraryHandle();
    return g_mediandk_LibraryHandle;
}
}

LOOKUP_FUNC(AMediaCodec_createDecoderByType,
    AMediaCodec*,
    (const char* mime_type),
    (mime_type));
LOOKUP_FUNC(AMediaCodec_createEncoderByType,
    AMediaCodec*,
    (const char* mime_type),
    (mime_type));
LOOKUP_FUNC(AMediaCodec_delete, media_status_t, (AMediaCodec * codec), (codec));
LOOKUP_FUNC(AMediaCodec_configure,
    media_status_t,
    (AMediaCodec * codec,
        const AMediaFormat* format,
        ANativeWindow* surface,
        AMediaCrypto* crypto,
        uint32_t flags),
    (codec, format, surface, crypto, flags));
LOOKUP_FUNC(AMediaCodec_start, media_status_t, (AMediaCodec * codec), (codec));
LOOKUP_FUNC(AMediaCodec_stop, media_status_t, (AMediaCodec * codec), (codec));
LOOKUP_FUNC(AMediaCodec_flush, media_status_t, (AMediaCodec * codec), (codec));
LOOKUP_FUNC(AMediaCodec_getInputBuffer,
    uint8_t*,
    (AMediaCodec * codec, size_t idx, size_t* out_size),
    (codec, idx, out_size));
LOOKUP_FUNC(AMediaCodec_getOutputBuffer,
    uint8_t*,
    (AMediaCodec * codec, size_t idx, size_t* out_size),
    (codec, idx, out_size));
LOOKUP_FUNC(AMediaCodec_dequeueInputBuffer,
    ssize_t,
    (AMediaCodec * codec, int64_t timeoutUs),
    (codec, timeoutUs));
LOOKUP_FUNC(AMediaCodec_queueInputBuffer,
    media_status_t,
    (AMediaCodec * codec,
        size_t idx,
        off_t offset,
        size_t size,
        uint64_t time,
        uint32_t flags),
    (codec, idx, offset, size, time, flags));
LOOKUP_FUNC(AMediaCodec_queueSecureInputBuffer,
    media_status_t,
    (AMediaCodec * codec,
        size_t idx,
        off_t offset,
        AMediaCodecCryptoInfo* cryto,
        uint64_t time,
        uint32_t flags),
    (codec, idx, offset, cryto, time, flags));
LOOKUP_FUNC(AMediaCodec_dequeueInputBuffer,
    ssize_t,
    (AMediaCodec * codec,
        AMediaCodecBufferInfo* info,
        int64_t timeoutUs),
    (codec, info, timeoutUs));
LOOKUP_FUNC(AMediaCodec_dequeueOutputBuffer,
    ssize_t,
    (AMediaCodec * codec,
        AMediaCodecBufferInfo* info,
        int64_t timeoutUs),
    (codec, info, timeoutUs));
LOOKUP_FUNC(AMediaCodec_getOutputFormat,
    AMediaFormat*,
    (AMediaCodec * codec),
    (codec));
LOOKUP_FUNC(AMediaCodec_releaseOutputBuffer,
    media_status_t,
    (AMediaCodec * codec, size_t idx, bool render),
    (codec, idx, render));
LOOKUP_FUNC(AMediaCodecCryptoInfo_new,
    AMediaCodecCryptoInfo*,
    (int num_subsamples,
        uint8_t key[16],
        uint8_t iv[16],
        cryptoinfo_mode_t mode,
        size_t* clear_bytes,
        size_t* encrypted_bytes),
    (num_subsamples, key, iv, mode, clear_bytes, encrypted_bytes));
LOOKUP_FUNC(AMediaCodecCryptoInfo_delete,
    media_status_t,
    (AMediaCodecCryptoInfo * info),
    (info));
LOOKUP_FUNC(AMediaFormat_delete,
    media_status_t,
    (AMediaFormat * format),
    (format));
LOOKUP_FUNC(AMediaFormat_getInt32,
    bool,
    (AMediaFormat * format, const char* name, int32_t* out),
    (format, name, out));
